# 变异算子
import random
from src.interpreter import dec2bin, bin2dec, in_range_float16, dec2bin16, bin2dec16


def byte_mutation(tensor_seed, m_type, n):
    """
        m_type包括：
            - 取反 reverse
            - 左移1位 left
            - 右移1位 right
            - 左移2位 left2
            - 右移2位 right2
            - 删除 delete
            - 增加 add
            - 随即替换 random
        n可取：0,1,2,3
    """
    if n not in range(0,4):
        return ''
    tensor = dec2bin(tensor_seed)
    if m_type == 'reverse':
        for i in range(8):
            lst = list(tensor)
            lst[n * 8 + i] = '1' if lst[n * 8 + i] == '0' else '0'
            tensor = ''.join(lst)
    elif m_type == 'left':
        lst = list(tensor)
        for i in range(7):
            lst[n * 8 + 6 - i] = lst[n * 8 + 7 - i]
        lst[n * 8 + 7] = '0'
        tensor = ''.join(lst)
    elif m_type == 'left2':
        lst = list(tensor)
        for i in range(6):
            lst[n * 8 + 5 - i] = lst[n * 8 + 7 - i]
        lst[n * 8 + 7] = '0'
        lst[n * 8 + 6] = '0'
        tensor = ''.join(lst)
    elif m_type == 'right':
        lst = list(tensor)
        for i in range(7):
            lst[n * 8 + i + 1] = lst[n * 8 + i]
        lst[n * 8] = lst[n * 8 + 1]
        tensor = ''.join(lst)
    elif m_type == 'right2':
        lst = list(tensor)
        for i in range(6):
            lst[n * 8 + i + 2] = lst[n * 8 + i]
        lst[n * 8 + 1] = lst[n * 8 + 2]
        lst[n * 8] = lst[n * 8 + 1]
        tensor = ''.join(tensor)
    elif m_type == 'delete':
        lst = list(tensor)
        lst[n*8: n*8 + 8] = 'X'
        for i in range(8):
            lst.append('1')
        tensor = ''.join(lst).replace('X', '')
    elif m_type == 'add':
        new_one = ''
        for i in range(8):
            new_one = new_one + str(random.randint(0,1))
        tensor = tensor[0:n * 8] + new_one + tensor[n*8:-8]
    elif m_type == 'random':
        lst = list(tensor)
        for i in range(8):
            lst[n*8+i] = str(random.randint(0,1))
        tensor = ''.join(lst)
    else:
        return ''
    if tensor != '' and not in_range_float16(bin2dec(tensor)):
        return byte_mutation_16(tensor_seed, m_type, n)
    else:
        return bin2dec(tensor)


def noise_mutation(tensor):
    random_pattern = random.randint(0,1)
    if random_pattern == 0:
        noise = random.gauss(0, 1)
        res = tensor + noise
        while not in_range_float16(res):
            noise = random.gauss(0, 1)
            res = tensor + noise
    else:
        noise = random.uniform(-1, 1)
        res = tensor + noise
        while not in_range_float16(res):
            noise = random.uniform(-1, 1)
            res = tensor + noise
    return res


def gauss_approximate_mutation(tensor):
    sigma_mult_3 = min(65504 - abs(tensor), abs(tensor) - 2e-14)
    sigma = sigma_mult_3 / 3
    gauss_approximate = random.gauss(tensor, sigma)
    while not in_range_float16(gauss_approximate):
        gauss_approximate = random.gauss(tensor, sigma)
    return gauss_approximate


def float_rand_mutation(tensor, k):
    rand_mode = random.randint(0, 1)
    r = random.uniform(0,1)
    if rand_mode == 0:
        res = tensor + (65504 - tensor) * k * r
        while not in_range_float16(res):
            r = random.uniform(0, 1)
            res = tensor + (65504 - tensor) * k * r
    else:
        res = tensor - (tensor + 65504) * k * r
        while not in_range_float16(res):
            r = random.uniform(0, 1)
            res = tensor - (tensor + 65504) * k * r
    return res


def byte_mutation_16(tensor_seed, m_type, n):
    tensor = dec2bin16(tensor_seed)
    if m_type == 'reverse':
        for i in range(4):
            lst = list(tensor)
            lst[n * 4 + i] = '1' if lst[n * 4 + i] == '0' else '0'
            tensor = ''.join(lst)
    elif m_type == 'left':
        lst = list(tensor)
        for i in range(3):
            lst[n * 4 + 2 - i] = lst[n * 4 + 3 - i]
        lst[n * 4 + 3] = '0'
        tensor = ''.join(lst)
    elif m_type == 'left2':
        lst = list(tensor)
        for i in range(2):
            lst[n * 4 + 1 - i] = lst[n * 4 + 3 - i]
        lst[n * 4 + 3] = '0'
        lst[n * 4 + 2] = '0'
        tensor = ''.join(lst)
    elif m_type == 'right':
        lst = list(tensor)
        for i in range(3):
            lst[n * 4 + i + 1] = lst[n * 4 + i]
        lst[n * 4] = lst[n * 4 + 1]
        tensor = ''.join(lst)
    elif m_type == 'right2':
        lst = list(tensor)
        for i in range(2):
            lst[n * 4 + i + 2] = lst[n * 4 + i]
        lst[n * 4 + 1] = lst[n * 4 + 2]
        lst[n * 4] = lst[n * 4 + 1]
        tensor = ''.join(tensor)
    elif m_type == 'delete':
        lst = list(tensor)
        lst[n*4: n*4 + 4] = 'X'
        for i in range(4):
            lst.append('1')
        tensor = ''.join(lst).replace('X', '')
    elif m_type == 'add':
        new_one = ''
        for i in range(4):
            new_one = new_one + str(random.randint(0,1))
        tensor = tensor[0:n * 4] + new_one + tensor[n*4:-4]
    elif m_type == 'random':
        lst = list(tensor)
        for i in range(4):
            lst[n*4+i] = str(random.randint(0,1))
        tensor = ''.join(lst)
    else:
        return ''
    if tensor != '' and tensor[1:6] == '00000':
        lst = list(tensor)
        lst[1:6] = '00001'
        tensor = ''.join(lst)
    elif tensor != '' and tensor[1:6] == '11111':
        lst = list(tensor)
        lst[1:6] = '11110'
        tensor = ''.join(lst)
    return bin2dec16(tensor)


def reverse_mutation(tensor):
    res = 4 / tensor
    while not in_range_float16(res):
        if tensor > 4:
            tensor -= 32
            res = 4 / tensor
        elif tensor < -4:
            tensor += 32
            res = 4 / tensor
        elif tensor > 0:
            res -= 32
        else:
            res += 32
    return res


# def data_adaption_after_mutation(tensor):
#     if tensor[1:9] == '11111111':
#         lst = list(tensor)
#         lst[1:9] = '11111110'
#         return ''.join(lst)
#     elif tensor[1:9] == '00000000':
#         lst = list(tensor)
#         lst[1:9] = '00000001'
#         return ''.join(lst)
#     else:
#         return tensor


# def bit_mutation(tensor, m_type, n):
#     """
#         m_type包括：
#             - 取反 reverse
#             - 删除 delete
#             - 增加 add
#         n可取：0-31
#     """
#     if n not in range(32):
#         return ''
#     if m_type == 'reverse':
#         lst = list(tensor)
#         lst[n] = '1' if lst[n] == '0' else '0'
#         tensor = ''.join(lst)
#     elif m_type == 'delete':
#         lst = list(tensor)
#         lst[n] = 'X'
#         tensor = ''.join(lst).replace('X', '')
#         tensor = tensor + '1'
#     elif m_type == 'add':
#         tensor = tensor[0:n] + str(random.randint(0,1)) + tensor[n:-1]
#     else:
#         return ''
#     return data_adaption_after_mutation(tensor)


# def gene_mutation(tensor1, tensor2, n):
#     if n not in range(4):
#         return tensor1
#     lst = list(tensor1)
#     lst[n * 8:n * 8 + 8] = tensor2[n * 8:n * 8 + 8]
#     tensor1 = ''.join(lst)
#     return data_adaption_after_mutation(tensor1)